package com.qianniu.napi.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.google.common.base.Strings;
import com.qianniu.napi.admin.entity.MerchantApp;
import com.qianniu.napi.admin.entity.User;
import com.qianniu.napi.admin.entity.wxbean.WxmappTemplate;
import com.qianniu.napi.admin.service.IRedisService;
import com.qianniu.napi.admin.service.IUserService;
import com.qianniu.napi.common.config.Constants;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by chenxuebin on 2017/8/10.
 */
public class ObjectUtil {

    private static final Logger logger = LoggerFactory.getLogger(ObjectUtil.class);

    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) {
        if (map == null)
            return null;

        try {
            Object obj = beanClass.newInstance();

            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                Method setter = property.getWriteMethod();
                if (setter != null) {
                    setter.invoke(obj, map.get(property.getName()));
                }
            }
            return obj;
        } catch (Exception e) {
            logger.error(">>> ObjectUtil mapToObject error", e);
        }

        return null;
    }

    public static Map<String, Object> objectToMap(Object obj) throws Exception {
        if (obj == null)
            return null;

        Map<String, Object> map = new HashMap<String, Object>();

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(obj) : null;
            map.put(key, value);
        }

        return map;
    }

    public static String getObjectField(Object obj, String field) {
        if (obj == null)
            return null;

        Map<String, Object> map = new HashMap<String, Object>();

        try {

            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (!key.equals(field)) continue;

                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                return getter != null ? getter.invoke(obj).toString() : null;
            }
        } catch (Exception e) {

        }

        return null;
    }

    public static int getByteSize(String content) {
        int size = 0;
        if (null != content) {
            try {
                // 汉字采用utf-8编码时占3个字节
                size = content.getBytes("utf-8").length;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return size;
    }

    /*
     * 输出指定长度数字+字母字符串
     */
    public static List<String> randomStrings(int length, int num) {

        List<String> results = new ArrayList<String>();

        for (int j = 0; j < num; j++) {
            String val = "";

            Random random = new Random();
            for (int i = 0; i < length; i++) {
                String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num"; // 输出字母还是数字

                if ("char".equalsIgnoreCase(charOrNum)) // 字符串
                {
                    int choice = random.nextInt(2) % 2 == 0 ? 65 : 97; //取得大写字母还是小写字母
                    val += (char) (choice + random.nextInt(26));
                } else if ("num".equalsIgnoreCase(charOrNum)) // 数字
                {
                    val += String.valueOf(random.nextInt(10));
                }
            }
            val = val.toLowerCase();
            if (results.contains(val)) {
                continue;
            } else {
                results.add(val);
            }
        }
        return results;
    }

    /*
     * 输出指定长度数字子串
     */
    public static List<String> randomNum(int length, int num) {

        List<String> results = new ArrayList<String>();

        for (int j = 0; j < num; j++) {
            String val = "";

            Random random = new Random();
            for (int i = 0; i < length; i++) {
                val += String.valueOf(random.nextInt(10));// 输出数字
            }
            val = val.toLowerCase();
            if (results.contains(val)) {
                continue;
            } else {
                results.add(val);
            }
        }
        return results;
    }

    // key = mid+"__"+asmpid+"__uid__"+ ObjectUtil.randomStrings(8,1).get(0)+"__"+ma.getAppid()+"__"+ma.getAppType()[+"__"+sid+subsid][+"##"+main mpid];
    public static String getAsmkey(User user) {
        String key = null;
        IRedisService redisService = (IRedisService) SpringUtil.getBean("redisServiceImpl");
        MerchantApp mapp = redisService.getMApp(user.getMpid());
        MerchantApp asmapp = redisService.getMApp(user.getAsmpid());

        if (user != null) {
            key = user.getMid() + "__" + user.getAsmpid() + "__" + user.getId() + "__" + ObjectUtil.randomStrings(10, 1).get(0) + "__" + asmapp.getAppid() + "__" + asmapp.getAppType();
            if (user.getSid() != null) key += "__" + user.getSid();
            if (user.getSubsid() != null) key += "__" + user.getSubsid();
        }
        if (mapp.getAsmpid() != null) key += "##" + user.getMpid();
        logger.info("========getAsmkey key=" + key);
        return key.replace("__null", "__");
    }

    // key = mid+"__"+mpid+"__uid__"+ ObjectUtil.randomStrings(8,1).get(0)+"__"+ma.getAppid()+"__"+ma.getAppType()[+"__"+sid+subsid][+"##"+main mpid];
    public static String getKey(User user) {
        String key = null;
        IRedisService redisService = (IRedisService) SpringUtil.getBean("redisServiceImpl");
        MerchantApp mapp = redisService.getMApp(user.getMpid());

        logger.info("======================================================getKey");
        logger.info("==================getKey user=" + JSON.toJSONString(user));
        if (user != null) {
            key = user.getMid() + "__" + user.getMpid() + "__" + user.getId() + "__" + ObjectUtil.randomStrings(10, 1).get(0) + "__" + mapp.getAppid() + "__" + mapp.getAppType();
            if (user.getSid() != null) key += "__" + user.getSid();
            if (user.getSubsid() != null) key += "__" + user.getSubsid();
            logger.info("======user!=null==getToken key=" + key);
        } else {
            key = getMid() + "__" + getMpid() + "__" + getUid() == null ? "" : getUid() + "__" + ObjectUtil.randomStrings(10, 1).get(0) + "__" + getAppid() + "__" + getAppType();
        }
        if (mapp.getAsmpid() != null) key += "##" + mapp.getAsmpid();
        logger.info("========getKey key=" + key);
        return key.replace("__null", "__");
    }

    public static String getKey(User user, Long mainmpid) {
        String key = null;
        IRedisService redisService = (IRedisService) SpringUtil.getBean("redisServiceImpl");
        MerchantApp mapp = redisService.getMApp(user.getMpid());

        logger.info("--------------------------------------------getKey");
        logger.info("----------------------getKey user=" + JSON.toJSONString(user));
        if (user != null) {
            key = user.getMid() + "__" + user.getMpid() + "__" + user.getId() + "__" + ObjectUtil.randomStrings(10, 1).get(0) + "__" + mapp.getAppid() + "__" + mapp.getAppType();
            if (user.getSid() != null) key += "__" + user.getSid();
            if (user.getSubsid() != null) key += "__" + user.getSubsid();
            logger.info("======user!=null==getToken key=" + key);
        } else {
            key = getMid() + "__" + getMpid() + "__" + getUid() == null ? "" : getUid() + "__" + ObjectUtil.randomStrings(10, 1).get(0) + "__" + getAppid() + "__" + getAppType();
        }
        if (mainmpid != null) key += "##" + mainmpid;
        logger.info("========getKey mainmpid key=" + key);
        return key.replace("__null", "__");
    }

    public static HttpSession getSession() {
        HttpSession session = null;
        try {
            session = getRequest().getSession();
        } catch (Exception e) {
        }
        return session;
    }

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attrs.getRequest();
    }

    public static String getMcode() {
        HttpSession s = getSession();
        if (s == null) return null;
        return s.getAttribute("mcode").toString();
    }

    public static String getAppid() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object appid = s.getAttribute("appid");
        return appid == null ? null : appid.toString();
    }

    public static Long getSid() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object sid = s.getAttribute("sid");
        return sid == null ? null : Long.valueOf(sid.toString());
    }

    public static Long getSubsid() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object subsid = s.getAttribute("subsid");
        return subsid == null ? null : Long.valueOf(subsid.toString());
    }

    public static Long getMpid() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object mpid = s.getAttribute("mpid");
        return mpid == null ? null : Long.valueOf(mpid.toString());
    }

    public static Long getMainmpid() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object mainmpid = s.getAttribute("mainmpid");
        return mainmpid == null ? null : Long.valueOf(mainmpid.toString());
    }

    public static Long getUid() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object uid = s.getAttribute("uid");
        logger.debug("----------------------------------------------------------------------------uid=" + uid);
        return uid == null ? null : Long.valueOf(uid.toString());
    }

    public static Long getMid() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object mid = s.getAttribute("mid");
        return mid == null ? null : Long.valueOf(mid.toString());
    }

    public static String getGuid() {
        HttpSession s = getSession();
        if (s == null) return null;
        String guid = s.getAttribute("guid") == null ? null : s.getAttribute("guid").toString();
        return guid;
    }

    public static String getAppType() {
        HttpSession s = getSession();
        if (s == null) return null;
        Object appType = s.getAttribute("appType");
        return appType == null ? "" : appType.toString();
    }

    public static String getIP(HttpServletRequest req) {
//
//        String ipAddress = req.getRemoteAddr();
//        if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
//            //根据网卡获取本机配置的IP地址
//            InetAddress inetAddress = null;
//            try {
//                inetAddress = InetAddress.getLocalHost();
//            } catch (UnknownHostException e) {
//                logger.error("getIP error", e);
//            }
//            ipAddress = inetAddress.getHostAddress();
//        }
//        //对于通过多个代理的情况，第一个IP为客户端真实的IP地址，多个IP按照','分割
//        if (null != ipAddress && ipAddress.length() > 15) {
//            if (ipAddress.indexOf(",") > 0) {
//                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
//            }
//        }
//        return ipAddress;
        return getIP();
    }

    public static String getIP() {
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        // 设置IP地址
        String ip = IPUtils.getIpAddr(request);
        return ip;
    }

    public static String getLocation() {
        String ip = getIP();
        if (Strings.isNullOrEmpty(ip)) return "";
        if (ip.equals("127.0.0.1")) return "局域网";
        return AddressUtils.getCityInfo(getIP());
    }

    public static boolean sendTemplateMsg(String token, WxmappTemplate template) {

        boolean flag = false;

        String requestUrl = Constants.WXMAPP_URL_TEMPLATE_SEND_MSG;
        requestUrl = requestUrl.replace("ACCESS_TOKEN", token);
        logger.info("---------------------------------------------------------template=" + JSON.toJSONString(template));
        String result = HttpUtils.httpPost(requestUrl, template.toJSON());

        JSONObject jsonResult = JSON.parseObject(result);
        if (jsonResult != null) {
            Integer errorCode = jsonResult.getInteger("errcode");
            String errorMessage = jsonResult.getString("errmsg");
            if (errorCode == 0) {
                flag = true;
            } else {
                logger.error("模板消息发送失败:" + errorCode + "," + errorMessage);
                flag = false;
            }
        }

        return flag;
    }

    public static JSONObject wxdectypt(String encryptedData, String iv, String code, Map map) {
        logger.info("----------------------------------------------------------------------------wxdectypt coming");
        logger.info("----------------------------------------------------------------------------encryptedData=" + encryptedData);
        logger.info("----------------------------------------------------------------------------iv=" + iv);
        logger.info("----------------------------------------------------------------------------code=" + code);

        JSONObject result = null;
        Integer expires_in = null;
        //授权（必填）
        Long mpid = getMpid();
        Boolean flag = false;
        IRedisService redisService = (IRedisService) SpringUtil.getBean("redisServiceImpl");
        String grant_type = "authorization_code";
        String rskey = Constants.REDIS_SESSION_KEY + mpid + "_" + getGuid();
        logger.info("===============wxdectypt rskey=" + rskey);
        String sessionKey = redisService.get(rskey);
        // 默认使用当前服务器缓存的sessionKey
        if (Strings.isNullOrEmpty(sessionKey) && !Strings.isNullOrEmpty(code)) {
            logger.info("---------------------------wxdectypt no cache and code=" + code);

            //////////////// 1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid ////////////////
            //请求参数
            MerchantApp mapp = redisService.getMApp(mpid);
            String params = "appid=" + mapp.getAppid() + "&secret=" + mapp.getAppsecret() + "&js_code=" + code + "&grant_type=" + grant_type;
            logger.info("-------------------------params=" + params);
            //发送请求
            String sr = HttpUtil.sendGet(Constants.WX_URL_JSCODE2SESSION, params);
            logger.debug("-------------------------sr=" + sr);
            //解析相应内容（转换成json对象）
            JSONObject json = JSONObject.parseObject(sr);
            //获取会话密钥（session_key）
            sessionKey = json.get("session_key").toString();
            json.getInteger("expires_in");
            expires_in = 7200;
            if (expires_in == null) redisService.set(rskey, sessionKey);
            else redisService.set(rskey, sessionKey, expires_in, TimeUnit.SECONDS);

            flag = true;
        }

        if (Strings.isNullOrEmpty(sessionKey) && Strings.isNullOrEmpty(code)) {
            return null;
        }

        logger.info("===============wxdectypt session_key=" + sessionKey);
        try {
//            result = AesUtil.decrypt(encryptedData, sessionKey, iv);
            result = decrypt(encryptedData, sessionKey, iv, rskey);
        } catch (Exception e) {
            logger.error("======wxdectypt error", e);
        }
        logger.info("-----------------------------------------------------------------after wxdectypt result=" + JSON.toJSONString(result));

        if (result != null) {
            if (map != null) map.put("rskey", rskey);
            if (map != null) map.put("sessionKey", sessionKey);
            if (expires_in != null && map != null) map.put("expires_in", expires_in);
            return result;
        }

        if (result == null && !flag && !Strings.isNullOrEmpty(code)) {
            logger.info("--------------------------------------cache key jiemi fail try to use code jiemi");

            // 使用缓存的sessonkey解密失败，重新通过接口获取并解密
            MerchantApp mapp = redisService.getMApp(mpid);
            String params = "appid=" + mapp.getAppid() + "&secret=" + mapp.getAppsecret() + "&js_code=" + code + "&grant_type=" + grant_type;
            logger.info("===============params=" + params);
            //发送请求
            String sr = HttpUtil.sendGet(Constants.WX_URL_JSCODE2SESSION, params);
            logger.debug(">>>sr=" + sr);
            //解析相应内容（转换成json对象）
            JSONObject json = JSONObject.parseObject(sr);
            //获取会话密钥（session_key）
            sessionKey = json.get("session_key").toString();
            expires_in = json.getInteger("expires_in");
            expires_in = 7200;
            if (expires_in == null) redisService.set(rskey, sessionKey);
            else redisService.set(rskey, sessionKey, expires_in, TimeUnit.SECONDS);

            try {
                result = decrypt(encryptedData, sessionKey, iv, rskey);
            } catch (Exception e) {
                logger.error("======wxdectypt error", e);
            }
            logger.info("-----------------------------------------------------------------flag=false wxdectypt result=" + JSON.toJSONString(result));
        }

        if (result != null) {
            if (map != null) map.put("rskey", rskey);
            if (map != null) map.put("sessionKey", sessionKey);
            if (expires_in != null && map != null) map.put("expires_in", expires_in);
        }
        logger.info("===============wxdectypt result=" + result);
        return result;
    }

    public static JSONObject decrypt(String data, String sessionkey, String iv, String rkey) {
        // 被加密的数据
        byte[] dataByte = Base64.decodeBase64(data);
        // 加密秘钥
        byte[] keyByte = Base64.decodeBase64(sessionkey);
        // 偏移量
        byte[] ivByte = Base64.decodeBase64(iv);

        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
//            if (ivByte.length % base != 0) {
//                int groups = ivByte.length / base + (ivByte.length % base != 0 ? 1 : 0);
//                byte[] temp = new byte[groups * base];
//                Arrays.fill(temp, (byte) 0);
//                System.arraycopy(ivByte, 0, temp, 0, ivByte.length);
//                ivByte = temp;
//            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(AesUtil.wxdecode(resultByte), "UTF-8");
                logger.debug("================test==ObjectUtil decrypt result="+result);
                return JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            logger.error("======ObjectUtil decrypt error", e);
            IRedisService redisService = (IRedisService) SpringUtil.getBean("redisServiceImpl");
            if (!Strings.isNullOrEmpty(rkey)) redisService.del(rkey);
        }
        return null;
    }

    public static String sortQuery(String path) {
        if (Strings.isNullOrEmpty(path) || path.indexOf("?") < 0) return path;
        String reslut = path.substring(0, path.indexOf("?") + 1);
        String left = path.substring(path.indexOf("?") + 1);
        String[] leftArr = left.split("&");
        int len = leftArr.length;
        Arrays.sort(leftArr);

//        TreeMap<String, String> map = new TreeMap<String, String>();
//        for(int i = 0; i < leftArr.length; i++){
//            map.put(leftArr[i],"1");
//        }
//        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
//        while(it.hasNext()) {
//            Map.Entry<String, String> entry = it.next();
//            reslut+=entry.getKey()+"&";
//        }
//        if(reslut.indexOf("&")>0){
//        }
        for (int i = 0; i < leftArr.length; i++) {
            if (i == len - 1) reslut += leftArr[i];
            else reslut += leftArr[i] + "&";
        }
        logger.info("---------------------------------------------------------------------------sortQuery path=" + reslut);
        return reslut;
    }

    public static Map<String, String> sortQueryMap(String path) {
        if (Strings.isNullOrEmpty(path) || path.indexOf("?") < 0) return null;

        Map<String, String> map = new HashMap<String, String>();
        String left = path.substring(path.indexOf("?") + 1);
        String[] leftArr = left.split("&");
        int len = leftArr.length;
        Arrays.sort(leftArr);
        for (int i = 0; i < leftArr.length; i++) {
            String[] arr = leftArr[i].split("=");
            map.put(arr[0], arr.length == 2 ? arr[1] : "");
        }
        logger.info("----------------------------------------------------------------------------sortQuery map=" + map);
        return map;
    }

    public static Long orderFanuid(String oriscene) {
        if(Strings.isNullOrEmpty(oriscene)) return null;
        Long fanuid = null;
        String[] oris = oriscene.split("@");
        if (oris.length >= 1) {
            String scene = oris[0].replace("null", "");
            logger.debug("-----------------------------------------------start to orderFanuid scene=" + scene);
            if (!Strings.isNullOrEmpty(scene) && !scene.equals("null")) {
                // scene format //uid+page+act+ywid
                //uid.toString() + "_jfzhifu_Q_" + ywid;
                try {
                    fanuid = Long.parseLong(scene.split("_")[0]);
                } catch (Exception e) {
                    logger.error("orderFanuid Long.parseLong fanuid error", e);
                }
            }
        }

        if (oris.length >= 2) {
            //9c7d2de6-d921-3cfd-f14d-3fcd89f270d8_miaosha_button_31005
            //9c7d2de6-d921-3cfd-f14d-3fcd89f270d8
            String ori = oris[1].replace("null", "");
            logger.debug("----------------------------------------------start to orderFanuid ori=" + ori);
            String guid = ori.split("_")[0];
            IUserService userService = (IUserService) SpringUtil.getBean("userServiceImpl");
            User user = userService.selectOne(new EntityWrapper<User>().setSqlSelect("id,guid").eq("guid", guid));
            if (user != null) fanuid = user.getId();
        }
        return fanuid;
    }

    public static void main(String[] args) {

//        DiseaseDO bean = new DiseaseDO();
//        bean.setReason("hehe");
//        System.out.println("result="+getObjectField(bean,"reason"));
//        System.out.println(RegexUtil.REGEX_PRECRIPTION.matcher("治疗肚子疼有什么方子推荐").find());

    }
}
